#include "StdAfx.h"
#include "Xml.h"

static const char * c_Split = "|";

Xml::Xml(void)
{
}

Xml::~Xml(void)
{
}

bool Xml::save(const wstring & f /* = "" */)
{
	wstring file = f.empty() ? mFile : f;
	string s;
	wstring2string(s, file);
	return mDoc.SaveFile(s.c_str());
}

bool Xml::open(const wstring & file)
{
	string s;
	wstring2string(s, file);
	mFile = file;
	return mDoc.LoadFile(s.c_str());
}


TiXmlElement * Xml::getElement(TiXmlElement * elem, const string & path, bool createElementIfNotExist /* = false */)
{
	if (!elem)
		return 0;

	vector<string> v;
	split(v, path, c_XmlSplit);
	size_t pathDeep = v.size();
	if (pathDeep < 1)
		return elem;

	TiXmlElement * directElem = elem;
	for (size_t i = 0; i < pathDeep; ++i)
	{
		TiXmlElement * tmp = 0;
		tmp = directElem->FirstChildElement(v[i].c_str());
		if (!tmp)
		{
			if (createElementIfNotExist)
			{
				tmp = new TiXmlElement(v[i].c_str());
				directElem->LinkEndChild(tmp);
				directElem = tmp;
			}
			else
				return 0;
		}
		else
			directElem = tmp;
	}

	return directElem;
}

TiXmlElement * Xml::getElement(const string & path, bool createElementIfNotExist /* = true */)
{
	vector<string> v;
	split(v, path, c_XmlSplit);
	if (v.size() < 1)
		return 0;

	TiXmlElement * elem = mDoc.FirstChildElement(v[0].c_str());
	if (!elem)
	{
		if (!createElementIfNotExist)
			return 0;
		else
		{
			elem = new TiXmlElement(v[0].c_str());
			mDoc.LinkEndChild(elem);
		}
	}

	string str;
	for (size_t i = 1; i < v.size(); ++i)
	{
		str += v[i];
		if (i + 1 < v.size())
			str += '|';
	}
	return getElement(elem, str, createElementIfNotExist);
}

void Xml::splitAttributePath(string & attribute, string & path, const string & fullPath)
{
	vector<string> v;
	split(v, fullPath, c_XmlSplit);
	
	size_t count = v.size();
	for (size_t i = 0; i + 1 < count; ++i)
	{
		path += v[i];
		if (i + 2 < count)
			path += c_XmlSplit;
	}

	if (count)
		attribute = v[count - 1];
}

void Xml::setValueEx(XmlElement * elem, const wstring & value)
{
	string v;
	wstring2string(v, value);
	if (elem->FirstChild())
		elem->ReplaceChild(elem->FirstChild(), TiXmlText(v.c_str()));
	else
	{
		TiXmlText * t = new TiXmlText(v.c_str());
		elem->LinkEndChild(t);
	}
}

void Xml::setValueEx(XmlElement * elem, int value, int radix /* = 10 */)
{
	string v;
	if (radix == 10)
		sprintf(v, "%d", value);
	else
		sprintf(v, "%X", value);

	if (elem->FirstChild())
		elem->ReplaceChild(elem->FirstChild(), TiXmlText(v.c_str()));
	else
	{
		TiXmlText * t = new TiXmlText(v.c_str());
		elem->LinkEndChild(t);
	}
}

bool Xml::setValue(const string & path, const wstring & value)
{
	TiXmlElement * elem = getElement(path, true);
	if (!elem)
		return false;
	
	setValueEx(elem, value);
	return true;
}

bool Xml::setValue(const string & path, int value, int radix /* = 10 */)
{
	TiXmlElement * elem = getElement(path, true);
	if (!elem)
		return false;

	setValueEx(elem, value, radix);
	return true;
}

bool Xml::setValue(XmlElement * baseElem, const string & path, const wstring & value)
{
	XmlElement * elem = getElement(baseElem, path, true);
	if (!elem)
		return false;

	setValueEx(elem, value);
	return true;
}

bool Xml::setValue(XmlElement * baseElem, const string & path, int value, int radix /* = 10 */)
{
	XmlElement * elem = getElement(baseElem, path, true);
	if (!elem)
		return false;

	setValueEx(elem, value, radix);
	return true;
}


bool Xml::setAttribute(const string & attributePath, const wstring & value)
{
	string attribute, path;
	splitAttributePath(attribute, path, attributePath);
	TiXmlElement * elem = getElement(path, true);
	if (!elem)
		return false;

	string theValue;
	wstring2string(theValue, value);
	elem->SetAttribute(attribute.c_str(), theValue.c_str());
	return true;
}

bool Xml::setAttribute(TiXmlElement * baseElem, const string & attributePath, const wstring & value)
{
	string attribute, path;
	splitAttributePath(attribute, path, attributePath);

	TiXmlElement * elem = getElement(baseElem, path, true);
	if (!elem)
		return false;

	string theValue;
	wstring2string(theValue, value);
	elem->SetAttribute(attribute.c_str(), theValue.c_str());
	return true;
}

bool Xml::setAttribute(const string & attributePath, int value)
{
	string attribute, path;
	splitAttributePath(attribute, path, attributePath);
	TiXmlElement * elem = getElement(path, true);
	if (!elem)
		return false;

	elem->SetAttribute(attribute.c_str(), value);
	return true;
}

bool Xml::setAttribute(TiXmlElement * baseElem, const string & attributePath, int value)
{
	string attribute, path;
	splitAttributePath(attribute, path, attributePath);
	TiXmlElement * elem = getElement(baseElem, path, true);
	if (!elem)
		return false;

	elem->SetAttribute(attribute.c_str(), value);
	return true;
}

bool Xml::getAttribute(const string & attributePath, wstring & value)
{
	string attribute, path;
	splitAttributePath(attribute, path, attributePath);
	TiXmlElement * elem = getElement(path, false);
	if (!elem)
		return false;

	const char * theValue = elem->Attribute(attribute.c_str());
	if (!theValue)
		return false;

	string2wstring(value, theValue);
	return true;
}

bool Xml::getAttribute(const TiXmlElement * baseElem, const string & attributePath, wstring & value)
{
	string attribute, path;
	splitAttributePath(attribute, path, attributePath);
	TiXmlElement * elem = getElement((TiXmlElement*)baseElem, path, false);
	if (!elem)
		return false;

	const char * theValue = elem->Attribute(attribute.c_str());
	if (!theValue)
		return false;

	string2wstring(value, theValue);
	return true;
}

bool Xml::getAttribute(const string & attributePath, void * value, size_t paraSize /* = sizeof(int) */, int radix /* = 10 */)
{
	string attribute, path;
	splitAttributePath(attribute, path, attributePath);
	TiXmlElement * elem = getElement(path, false);
	if (!elem)
		return false;

	const char * theValue = elem->Attribute(attribute.c_str());
	if (theValue)
	{
		long tmp = strtol(theValue, 0, radix);
		memcpy(value, &tmp, paraSize);
		return true;
	}
	else
		return false;
}

bool Xml::getAttribute(const TiXmlElement * baseElem, const string & attributePath, void * value, size_t paraSize /* = sizeof(int) */, int radix /* = 10 */)
{
	string attribute, path;
	splitAttributePath(attribute, path, attributePath);
	TiXmlElement * elem = getElement((TiXmlElement*)baseElem, path, false);
	if (!elem)
		return false;

	const char * theValue = elem->Attribute(attribute.c_str());
	if (theValue)
	{
		long tmp = strtol(theValue, 0, radix);
		memcpy(value, &tmp, paraSize);
		return true;
	}
	else
		return false;
}

bool Xml::getValue(const string & path, void * value, size_t paraSize /* = sizeof(int) */, int radix /* = 10 */)
{
	TiXmlElement * elem = getElement(path, false);
	if (!elem)
		return false;

	const char * theValue = elem->GetText();
	if (theValue)
	{
		long tmp = strtol(theValue, 0, radix);
		memcpy(value, &tmp, paraSize);
		return true;
	}
	else
		return false;
}

bool Xml::getValue(const string & path, wstring & value)
{
	TiXmlElement * elem = getElement(path, false);
	if (!elem)
		return false;
	
	const char * theValue = elem->GetText();
	if (!theValue)
		return false;

	string2wstring(value, theValue);
	return true;
}

bool Xml::getValue(const TiXmlElement * baseElem, const string & path, void * value, size_t paraSize /* = sizeof(int) */, int radix /* = 10 */)
{
	TiXmlElement * elem = getElement((TiXmlElement*)baseElem, path, false);
	if (!elem)
		return false;

	const char * theValue = elem->GetText();
	if (theValue)
	{
		long tmp = strtol(theValue, 0, radix);
		memcpy(value, &tmp, paraSize);
		return true;
	}
	else
		return false;
}

bool Xml::getValue(const TiXmlElement * baseElem, const string & path, wstring & value)
{
	TiXmlElement * elem = getElement((TiXmlElement*)baseElem, path, false);
	if (!elem)
		return false;

	const char * theValue = elem->GetText();
	if (!theValue)
		return false;

	string2wstring(value, theValue);
	return true;
}

bool Xml::removeAllChild(TiXmlElement * elem)
{
	while (1)
	{
		TiXmlElement * sub = elem->FirstChildElement();
		if (!sub)
			break;

		if (!elem->RemoveChild(sub))
			return false;
	}

	return true;
}

void Xml::formatNodeName(string & name)
{
	trim(name);

	for (size_t i = 0; i < name.size(); )
	{
		char c = name.at(i);
		bool forward = true;
		if (!isdigit(c) && !isalpha(c))
		{
			if (i != 0 && name[i - 1] != '_')
				name[i] = '_';
			else
			{
				name.erase(i, 1);
				forward = false;
			}
		}
		if (forward)
			++i;
	}
}

bool Xml::saveAttribute(const wstring & file, const string & path, const wstring & value)
{
	Xml xml;
	xml.open(file);

	if (!xml.setAttribute(path, value))
		return false;

	xml.save(file);
	return true;
}
